<!DOCTYPE html>
<html lang="zh-CN">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>ES索引迁移工具</title>
    <style>
        * {
            margin: 0;
            padding: 0;
            box-sizing: border-box;
        }

        body {
            font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif;
            background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
            min-height: 100vh;
            padding: 20px;
        }

        .container {
            max-width: 1200px;
            margin: 0 auto;
            background: white;
            border-radius: 20px;
            box-shadow: 0 20px 40px rgba(0,0,0,0.1);
            overflow: hidden;
        }

        .header {
            background: linear-gradient(135deg, #4facfe 0%, #00f2fe 100%);
            color: white;
            padding: 30px;
            text-align: center;
        }

        .header h1 {
            font-size: 2.5em;
            margin-bottom: 10px;
        }

        .main-content {
            display: grid;
            grid-template-columns: 1fr 1fr;
            gap: 30px;
            padding: 30px;
        }

        .config-section {
            background: #f8f9fa;
            border-radius: 15px;
            padding: 25px;
            border: 2px solid #e9ecef;
        }

        .config-section h3 {
            color: #495057;
            margin-bottom: 20px;
            font-size: 1.3em;
        }

        .form-group {
            margin-bottom: 20px;
        }

        .form-group label {
            display: block;
            margin-bottom: 5px;
            font-weight: 600;
            color: #495057;
        }

        .form-group input, .form-group select {
            width: 100%;
            padding: 12px;
            border: 2px solid #dee2e6;
            border-radius: 8px;
            font-size: 16px;
        }

        .btn {
            padding: 15px 30px;
            border: none;
            border-radius: 50px;
            font-size: 16px;
            font-weight: 600;
            cursor: pointer;
            min-width: 150px;
            margin: 10px;
        }

        .btn-primary {
            background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
            color: white;
        }

        .log-section {
            grid-column: 1 / -1;
            background: #1e1e1e;
            border-radius: 15px;
            margin-top: 30px;
            overflow: hidden;
        }

        .log-content {
            height: 400px;
            overflow-y: auto;
            padding: 20px;
            font-family: 'Courier New', monospace;
            color: #f8f9fa;
            font-size: 14px;
        }

        .tab-container {
            margin-top: 20px;
        }

        .tabs {
            display: flex;
            background: #f8f9fa;
            border-radius: 10px 10px 0 0;
            overflow: hidden;
        }

        .tab {
            flex: 1;
            padding: 15px 20px;
            background: #e9ecef;
            border: none;
            cursor: pointer;
            font-weight: 600;
            transition: background-color 0.3s;
        }

        .tab.active {
            background: #667eea;
            color: white;
        }

        .tab-content {
            display: none;
            background: white;
            padding: 20px;
            border-radius: 0 0 10px 10px;
            border: 2px solid #e9ecef;
            border-top: none;
        }

        .tab-content.active {
            display: block;
        }

        .indices-list {
            max-height: 300px;
            overflow-y: auto;
            border: 2px solid #dee2e6;
            border-radius: 8px;
            padding: 10px;
            margin: 10px 0;
        }

        .index-item {
            display: flex;
            align-items: center;
            padding: 8px;
            border-bottom: 1px solid #f1f3f4;
            cursor: pointer;
        }

        .index-item:hover {
            background-color: #f8f9fa;
        }

        .index-item input[type="checkbox"] {
            margin-right: 10px;
        }

        .task-list {
            max-height: 400px;
            overflow-y: auto;
        }

        .task-item {
            background: #f8f9fa;
            border-radius: 8px;
            padding: 15px;
            margin-bottom: 10px;
            border-left: 4px solid #667eea;
        }

        .task-item.completed {
            border-left-color: #28a745;
        }

        .task-item.failed {
            border-left-color: #dc3545;
        }

        .task-item.running {
            border-left-color: #ffc107;
        }

        .task-header {
            display: flex;
            justify-content: space-between;
            align-items: center;
            margin-bottom: 10px;
        }

        .task-status {
            padding: 4px 8px;
            border-radius: 4px;
            font-size: 12px;
            font-weight: bold;
            text-transform: uppercase;
        }

        .status-pending {
            background: #e9ecef;
            color: #6c757d;
        }

        .status-running {
            background: #fff3cd;
            color: #856404;
        }

        .status-completed {
            background: #d4edda;
            color: #155724;
        }

        .status-failed {
            background: #f8d7da;
            color: #721c24;
        }

        .progress-bar {
            width: 100%;
            height: 8px;
            background: #e9ecef;
            border-radius: 4px;
            overflow: hidden;
            margin: 10px 0;
        }

        .progress-fill {
            height: 100%;
            background: linear-gradient(90deg, #667eea, #764ba2);
            transition: width 0.3s ease;
        }

        .btn-group {
            display: flex;
            gap: 10px;
            flex-wrap: wrap;
            justify-content: center;
        }

        .btn-small {
            padding: 8px 16px;
            font-size: 14px;
            min-width: auto;
        }

        /* 添加模态框样式 */
        .modal {
            display: none;
            position: fixed;
            z-index: 1000;
            left: 0;
            top: 0;
            width: 100%;
            height: 100%;
            background-color: rgba(0,0,0,0.5);
        }

        .modal-content {
            background-color: #1a1a1a;
            margin: 5% auto;
            padding: 20px;
            border: 1px solid #444;
            border-radius: 8px;
            width: 80%;
            max-width: 800px;
            position: relative;
        }

        .modal-header {
            display: flex;
            justify-content: space-between;
            align-items: center;
            margin-bottom: 20px;
        }

        .modal-header h3 {
            margin: 0;
            color: #fff;
        }

        .close {
            color: #aaa;
            font-size: 28px;
            font-weight: bold;
            cursor: pointer;
        }

        .close:hover {
            color: #fff;
        }

        .modal-body {
            color: #ddd;
        }

        #queryDsl {
            background-color: #2d2d2d;
            color: #fff;
            border: 1px solid #444;
            padding: 10px;
            width: 100%;
            margin-top: 5px;
        }

        pre {
            background-color: #2d2d2d;
            padding: 10px;
            border-radius: 4px;
            overflow-x: auto;
        }
    </style>
    <!-- 添加模态框样式 -->
    <style>
        .modal {
            display: none;
            position: fixed;
            z-index: 1000;
            left: 0;
            top: 0;
            width: 100%;
            height: 100%;
            background-color: rgba(0,0,0,0.5);
            overflow-y: auto;
            padding: 20px;
        }

        .modal-content {
            background-color: #2d3748;
            margin: 20px auto;
            padding: 0;
            border: none;
            border-radius: 12px;
            width: 90%;
            max-width: 900px;
            box-shadow: 0 4px 6px rgba(0, 0, 0, 0.1);
        }

        .modal-header {
            background-color: #1a202c;
            padding: 20px;
            border-radius: 12px 12px 0 0;
            border-bottom: 1px solid #4a5568;
        }

        .modal-header h3 {
            margin: 0;
            color: #fff;
            font-size: 1.5rem;
        }

        .close {
            color: #a0aec0;
            font-size: 24px;
            font-weight: bold;
            cursor: pointer;
            transition: color 0.2s;
            padding: 5px 10px;
        }

        .close:hover {
            color: #fff;
        }

        .modal-body {
            padding: 20px;
            color: #e2e8f0;
            max-height: calc(100vh - 200px);
            overflow-y: auto;
        }

        #queryDsl {
            background-color: #1a202c;
            color: #e2e8f0;
            border: 1px solid #4a5568;
            border-radius: 6px;
            padding: 15px;
            width: 100%;
            margin-top: 10px;
            font-family: 'Monaco', 'Menlo', 'Ubuntu Mono', 'Consolas', monospace;
            font-size: 14px;
            line-height: 1.5;
            resize: vertical;
        }

        #queryDsl:focus {
            outline: none;
            border-color: #63b3ed;
            box-shadow: 0 0 0 3px rgba(66, 153, 225, 0.5);
        }

        .query-examples {
            margin-top: 25px;
            background-color: #1a202c;
            border-radius: 8px;
            padding: 20px;
        }

        .query-examples h5 {
            color: #fff;
            margin-top: 0;
            margin-bottom: 15px;
            font-size: 1.1rem;
        }

        .example-item {
            margin-bottom: 20px;
        }

        .example-item p {
            color: #a0aec0;
            margin: 0 0 8px 0;
            font-size: 0.9rem;
        }

        pre {
            background-color: #2d3748;
            padding: 15px;
            border-radius: 6px;
            overflow-x: auto;
            margin: 0;
            border: 1px solid #4a5568;
        }

        pre code {
            color: #e2e8f0;
            font-family: 'Monaco', 'Menlo', 'Ubuntu Mono', 'Consolas', monospace;
            font-size: 13px;
        }

        .modal-footer {
            padding: 20px;
            border-top: 1px solid #4a5568;
            display: flex;
            justify-content: flex-end;
            gap: 10px;
        }

        .btn-secondary {
            background-color: #4a5568;
            color: #fff;
            border: none;
            padding: 8px 16px;
            border-radius: 6px;
            cursor: pointer;
            transition: background-color 0.2s;
        }

        .btn-secondary:hover {
            background-color: #2d3748;
        }

        .btn-primary {
            background-color: #4299e1;
            color: #fff;
            border: none;
            padding: 8px 16px;
            border-radius: 6px;
            cursor: pointer;
            transition: background-color 0.2s;
        }

        .btn-primary:hover {
            background-color: #3182ce;
        }
    </style>
</head>
<body>
    <div class="container">
        <div class="header">
            <h1>🚀 ES索引迁移工具</h1>
            <p>智能ES索引结构迁移 (支持多版本兼容性)</p>
        </div>

        <div class="main-content">
            <!-- 源ES配置 -->
            <div class="config-section">
                <h3>⚙️ 源ES配置</h3>
                <div class="form-group">
                    <label for="sourceFullUrl">完整地址 (可选)</label>
                    <input type="text" id="sourceFullUrl" placeholder="例如: https://user:pass@host:port/path">
                </div>
                <div class="form-group">
                    <label>或者分别填写以下信息:</label>
                </div>
                <div class="form-group">
                    <label for="sourceHost">主机地址</label>
                    <input type="text" id="sourceHost" value="localhost">
                </div>
                <div class="form-group">
                    <label for="sourcePort">端口</label>
                    <input type="number" id="sourcePort" value="9200">
                </div>
                <div class="form-group">
                    <label for="sourceUsername">用户名 (可选)</label>
                    <input type="text" id="sourceUsername">
                </div>
                <div class="form-group">
                    <label for="sourcePassword">密码 (可选)</label>
                    <input type="password" id="sourcePassword">
                </div>
            </div>

            <!-- 目标ES配置 -->
            <div class="config-section">
                <h3>⚙️ 目标ES配置</h3>
                <div class="form-group">
                    <label for="targetFullUrl">完整地址 (可选)</label>
                    <input type="text" id="targetFullUrl" placeholder="例如: https://user:pass@host:port/path">
                </div>
                <div class="form-group">
                    <label>或者分别填写以下信息:</label>
                </div>
                <div class="form-group">
                    <label for="targetHost">主机地址</label>
                    <input type="text" id="targetHost" value="localhost">
                </div>
                <div class="form-group">
                    <label for="targetPort">端口</label>
                    <input type="number" id="targetPort" value="9200">
                </div>
                <div class="form-group">
                    <label for="targetUsername">X-PACK用户名</label>
                    <input type="text" id="targetUsername" value="elastic">
                </div>
                <div class="form-group">
                    <label for="targetPassword">X-PACK密码</label>
                    <input type="password" id="targetPassword">
                </div>
            </div>

            <!-- 操作按钮 -->
            <div style="grid-column: 1 / -1;">
                <div class="btn-group">
                    <button class="btn btn-primary" onclick="testConnection()">🔍 测试连接</button>
                    <button class="btn btn-primary" onclick="previewMigration()">👁️ 预览迁移</button>
                </div>
            </div>

            <!-- 标签页容器 -->
            <div class="tab-container" style="grid-column: 1 / -1;">
                <div class="tabs">
                    <button class="tab active" onclick="switchTab('migration')">📦 迁移操作</button>
                    <button class="tab" onclick="switchTab('tasks')">📋 任务管理</button>
                    <button class="tab" onclick="switchTab('logs')">📄 实时日志</button>
                </div>

                <!-- 迁移操作标签页 -->
                <div id="migration-tab" class="tab-content active">
                    <h3>📦 迁移操作</h3>
                    
                    <!-- 索引选择区域 -->
                    <div id="indexSelection" style="display: none;">
                        <h4>选择要迁移的索引：</h4>
                        <div style="margin-bottom: 10px;">
                            <button class="btn btn-small" onclick="selectAllIndices()">全选</button>
                            <button class="btn btn-small" onclick="unselectAllIndices()">全不选</button>
                            <span id="selectedCount" style="margin-left: 10px;">已选择: 0</span>
                        </div>
                        <div id="indicesList" class="indices-list">
                            <!-- 索引列表将在这里动态生成 -->
                        </div>
                    </div>

                    <div class="btn-group" style="margin-top: 20px;">
                        <button class="btn btn-primary" onclick="startSelectiveMigration()">🚀 开始选择性迁移</button>
                        <button class="btn btn-primary" onclick="startReindex()">🔄 开始Reindex</button>
                    </div>
                </div>

                <!-- 任务管理标签页 -->
                <div id="tasks-tab" class="tab-content">
                    <div style="display: flex; justify-content: space-between; align-items: center; margin-bottom: 20px;">
                        <h3>📋 任务管理</h3>
                        <button class="btn btn-small" onclick="refreshTasks()">🔄 刷新</button>
                    </div>
                    <div id="tasksList" class="task-list">
                        <!-- 任务列表将在这里动态生成 -->
                    </div>
                </div>

                <!-- 实时日志标签页 -->
                <div id="logs-tab" class="tab-content">
                    <h3>📄 实时日志</h3>
                    <div class="log-section">
                        <div class="log-content" id="logContent">
                            <div>系统已启动，等待用户操作...</div>
                        </div>
                    </div>
                </div>
            </div>
        </div>
    </div>

    <!-- 任务详情弹窗 -->
    <div id="taskDetailsModal" style="display: none; position: fixed; top: 0; left: 0; width: 100%; height: 100%; background: rgba(0,0,0,0.5); z-index: 1000;">
        <div style="position: absolute; top: 50%; left: 50%; transform: translate(-50%, -50%); background: white; border-radius: 8px; padding: 20px; max-width: 900px; width: 90%; max-height: 80%; overflow-y: auto;">
            <div style="display: flex; justify-content: space-between; align-items: center; margin-bottom: 20px;">
                <h3 style="margin: 0; color: #333;">📋 任务详情</h3>
                <button onclick="closeTaskDetails()" style="background: #dc3545; color: white; border: none; border-radius: 4px; padding: 5px 10px; cursor: pointer;">关闭</button>
            </div>
            <div id="taskDetailsContent" style="color: #333;">
                <!-- 动态内容将在这里显示 -->
            </div>
        </div>
    </div>

    <!-- 索引结构模态框 -->
    <div id="indexStructureModal" style="display: none; position: fixed; top: 0; left: 0; width: 100%; height: 100%; background: rgba(0,0,0,0.5); z-index: 1000;">
        <div style="position: absolute; top: 50%; left: 50%; transform: translate(-50%, -50%); background: white; border-radius: 8px; padding: 20px; max-width: 900px; width: 90%; max-height: 80%; overflow-y: auto;">
            <div style="display: flex; justify-content: space-between; align-items: center; margin-bottom: 20px;">
                <h3 style="margin: 0; color: #333;">📊 索引结构</h3>
                <button onclick="closeIndexStructure()" style="background: #dc3545; color: white; border: none; border-radius: 4px; padding: 5px 10px; cursor: pointer;">关闭</button>
            </div>
            <div id="indexStructureContent" style="color: #333;">
                <div style="margin-bottom: 20px;">
                    <h4 id="structureIndexName" style="color: #4a5568; margin-bottom: 15px;"></h4>
                    <div class="tabs" style="margin-bottom: 15px;">
                        <button class="tab active" onclick="switchStructureTab('mapping')" id="mappingTab">映射和设置</button>
                        <button class="tab" onclick="switchStructureTab('sample')" id="sampleTab">最新数据</button>
                    </div>
                    <div id="mappingContent" class="tab-content active" style="background: #f7fafc; border-radius: 8px; padding: 15px;">
                        <pre id="structureContent" style="margin: 0; white-space: pre-wrap; font-family: monospace;"></pre>
                    </div>
                    <div id="sampleContent" class="tab-content" style="background: #f7fafc; border-radius: 8px; padding: 15px;">
                        <pre id="sampleDataContent" style="margin: 0; white-space: pre-wrap; font-family: monospace;"></pre>
                    </div>
                </div>
            </div>
        </div>
    </div>

    <!-- 在body中添加查询条件模态框 -->
    <div id="queryModal" class="modal">
        <div class="modal-content">
            <div class="modal-header">
                <h3>设置 Reindex 查询条件</h3>
                <span class="close" onclick="closeQueryModal()">&times;</span>
            </div>
            <div class="modal-body">
                <div class="form-group">
                    <label for="queryDsl" style="font-weight: 500; color: #fff;">Elasticsearch 查询 DSL (可选):</label>
                    <textarea id="queryDsl" rows="10" placeholder='{
    "query": {
        "bool": {
            "must": [
                { "term": { "status": "active" } },
                { "range": { "timestamp": { "gte": "2024-01-01" } } }
            ]
        }
    }
}'></textarea>
                </div>
                <div class="query-examples">
                    <h5>📝 查询示例</h5>
                    <div class="example-item">
                        <p>1. 按时间范围筛选:</p>
                        <pre><code>{
    "query": {
        "range": {
            "timestamp": {
                "gte": "2024-01-01",
                "lt": "2024-04-01"
            }
        }
    }
}</code></pre>
                    </div>
                    <div class="example-item">
                        <p>2. 按字段值筛选:</p>
                        <pre><code>{
    "query": {
        "term": {
            "status": "active"
        }
    }
}</code></pre>
                    </div>
                    <div class="example-item">
                        <p>3. 组合条件筛选:</p>
                        <pre><code>{
    "query": {
        "bool": {
            "must": [
                { "term": { "status": "active" } },
                { "range": { "age": { "gte": 18 } } }
            ],
            "must_not": [
                { "term": { "deleted": true } }
            ]
        }
    }
}</code></pre>
                    </div>
                </div>
            </div>
            <div class="modal-footer">
                <button class="btn btn-secondary" onclick="clearQuery()">清除查询</button>
                <button class="btn btn-primary" onclick="validateAndStartReindex()">确认开始 Reindex</button>
            </div>
        </div>
    </div>

    <script src="https://cdnjs.cloudflare.com/ajax/libs/socket.io/4.0.1/socket.io.js"></script>
    <script>
        const socket = io();
        let currentIndices = [];
        let selectedIndices = [];
        
        // 格式化日期时间
        function formatDateTime(date) {
            const pad = (n) => n.toString().padStart(2, '0');
            return `${date.getFullYear()}-${pad(date.getMonth() + 1)}-${pad(date.getDate())} ${pad(date.getHours())}:${pad(date.getMinutes())}:${pad(date.getSeconds())}.${pad(date.getMilliseconds()).slice(0,3)}`;
        }

        // 格式化数字（添加千位分隔符）
        function formatNumber(num) {
            return num.toString().replace(/\B(?=(\d{3})+(?!\d))/g, ",");
        }

        // 格式化字节大小
        function formatBytes(bytes) {
            if (bytes === 0) return '0 B';
            const k = 1024;
            const sizes = ['B', 'KB', 'MB', 'GB', 'TB'];
            const i = Math.floor(Math.log(bytes) / Math.log(k));
            return parseFloat((bytes / Math.pow(k, i)).toFixed(2)) + ' ' + sizes[i];
        }

        // 格式化持续时间
        function formatDuration(milliseconds) {
            if (milliseconds < 1000) return milliseconds + 'ms';
            const seconds = Math.floor(milliseconds / 1000);
            const minutes = Math.floor(seconds / 60);
            const hours = Math.floor(minutes / 60);
            
            if (hours > 0) {
                return `${hours}h ${minutes % 60}m ${seconds % 60}s`;
            } else if (minutes > 0) {
                return `${minutes}m ${seconds % 60}s`;
            } else {
                return `${seconds}s`;
            }
        }

        // 更新日志显示
        function updateLog(message) {
            const logContainer = document.getElementById('logContainer');
            const now = new Date();
            let logType = 'info';
            let details = null;
            let progress = null;
            let stats = null;

            // 解析消息类型和内容
            if (typeof message === 'object') {
                if (message.error) {
                    logType = 'error';
                    message = message.error;
                } else if (message.type) {
                    logType = message.type;
                    
                    if (message.progress) {
                        progress = {
                            current: message.progress.current,
                            total: message.progress.total,
                            percent: Math.round((message.progress.current / message.progress.total) * 100)
                        };
                    }
                    
                    if (message.stats) {
                        stats = message.stats;
                    }
                    
                    if (message.details) {
                        details = message.details;
                    }
                    
                    message = message.message;
                }
            }

            // 创建日志条目
            const logEntry = document.createElement('div');
            logEntry.className = 'log-entry';
            
            // 添加时间戳
            const timeSpan = document.createElement('span');
            timeSpan.className = 'log-time';
            timeSpan.textContent = formatDateTime(now);
            logEntry.appendChild(timeSpan);
            
            // 添加日志类型标签
            const typeSpan = document.createElement('span');
            typeSpan.className = `log-type log-type-${logType}`;
            typeSpan.textContent = logType;
            logEntry.appendChild(typeSpan);
            
            // 添加主要消息
            const messageDiv = document.createElement('div');
            messageDiv.className = 'log-message';
            messageDiv.textContent = message;
            
            // 如果有详细信息，添加到消息下方
            if (details) {
                const detailsDiv = document.createElement('div');
                detailsDiv.className = 'log-details';
                detailsDiv.textContent = details;
                messageDiv.appendChild(detailsDiv);
            }
            
            // 如果有进度信息，添加进度条
            if (progress) {
                const progressDiv = document.createElement('div');
                progressDiv.className = 'log-progress';
                const progressBar = document.createElement('div');
                progressBar.className = 'log-progress-bar';
                progressBar.style.width = `${progress.percent}%`;
                progressDiv.appendChild(progressBar);
                messageDiv.appendChild(progressDiv);
                
                const statsDiv = document.createElement('div');
                statsDiv.className = 'log-stats';
                statsDiv.innerHTML = `
                    <div class="log-stat-item">
                        <span class="log-stat-label">进度:</span>
                        ${formatNumber(progress.current)} / ${formatNumber(progress.total)} (${progress.percent}%)
                    </div>
                `;
                messageDiv.appendChild(statsDiv);
            }
            
            // 如果有统计信息，添加到消息下方
            if (stats) {
                const statsDiv = document.createElement('div');
                statsDiv.className = 'log-stats';
                statsDiv.innerHTML = `
                    <div class="log-stat-item">
                        <span class="log-stat-label">处理文档:</span>
                        ${formatNumber(stats.processed_docs)}
                    </div>
                    <div class="log-stat-item">
                        <span class="log-stat-label">处理大小:</span>
                        ${formatBytes(stats.processed_size)}
                    </div>
                    <div class="log-stat-item">
                        <span class="log-stat-label">耗时:</span>
                        ${formatDuration(stats.duration)}
                    </div>
                    <div class="log-stat-item">
                        <span class="log-stat-label">速度:</span>
                        ${formatNumber(stats.docs_per_second)} docs/s
                    </div>
                `;
                messageDiv.appendChild(statsDiv);
            }
            
            logEntry.appendChild(messageDiv);
            logContainer.appendChild(logEntry);
            
            // 自动滚动到底部
            logContainer.scrollTop = logContainer.scrollHeight;
        }

        // 更新任务状态和进度
        function updateTaskStatus(taskId) {
            fetch('/api/task_status/' + taskId)
                .then(response => response.json())
                .then(data => {
                    if (data.status) {
                        // 更新状态显示
                        const statusText = {
                            'running': '正在运行',
                            'completed': '已完成',
                            'failed': '失败',
                            'cancelled': '已取消'
                        }[data.status] || data.status;
                        
                        document.getElementById('taskStatus').textContent = statusText;
                        
                        // 添加详细日志
                        if (data.logs && Array.isArray(data.logs)) {
                            data.logs.forEach(log => {
                                updateLog({
                                    type: log.level || 'info',
                                    message: log.message,
                                    progress: log.progress,
                                    stats: log.stats,
                                    details: log.details
                                });
                            });
                        }
                        
                        // 如果任务仍在运行，继续轮询
                        if (data.status === 'running') {
                            setTimeout(() => updateTaskStatus(taskId), 1000);
                        }
                    }
                })
                .catch(error => {
                    console.error('获取任务状态失败:', error);
                    updateLog({
                        type: 'error',
                        message: '获取任务状态失败: ' + error.message
                    });
                });
        }

        socket.on('log_message', function(data) {
            const logContent = document.getElementById('logContent');
            const div = document.createElement('div');
            div.style.marginBottom = '5px';
            div.style.padding = '5px 10px';
            div.style.borderRadius = '3px';
            
            // 根据日志级别设置不同颜色
            switch(data.level) {
                case 'INFO':
                    div.style.color = '#90CDF4';  // 浅蓝色
                    break;
                case 'WARNING':
                    div.style.color = '#F6E05E';  // 黄色
                    div.style.backgroundColor = 'rgba(255, 193, 7, 0.1)';
                    break;
                case 'ERROR':
                    div.style.color = '#FC8181';  // 红色
                    div.style.backgroundColor = 'rgba(220, 53, 69, 0.1)';
                    break;
                case 'PROGRESS':
                    div.style.color = '#9AE6B4';  // 绿色
                    break;
                default:
                    div.style.color = '#f8f9fa';  // 白色
            }

            let logMessage = `[${data.timestamp}] [${data.level}] ${data.message}`;
            
            // 如果有处理进度信息，添加到日志中
            if (data.progress) {
                const { current, total } = data.progress;
                const percent = ((current / total) * 100).toFixed(2);
                logMessage += `\n处理进度: ${current.toLocaleString()} / ${total.toLocaleString()} (${percent}%)`;
            }
            
            // 如果有统计信息，添加到日志中
            if (data.stats) {
                const { processed_docs, processed_size, duration, docs_per_second } = data.stats;
                
                // 格式化处理大小
                const formatSize = (bytes) => {
                    if (bytes === 0) return '0 B';
                    const k = 1024;
                    const sizes = ['B', 'KB', 'MB', 'GB', 'TB'];
                    const i = Math.floor(Math.log(bytes) / Math.log(k));
                    return parseFloat((bytes / Math.pow(k, i)).toFixed(2)) + ' ' + sizes[i];
                };
                
                // 格式化持续时间
                const formatDuration = (ms) => {
                    if (ms < 1000) return ms + 'ms';
                    const seconds = Math.floor(ms / 1000);
                    const minutes = Math.floor(seconds / 60);
                    const hours = Math.floor(minutes / 60);
                    
                    if (hours > 0) {
                        return `${hours}h ${minutes % 60}m ${seconds % 60}s`;
                    } else if (minutes > 0) {
                        return `${minutes}m ${seconds % 60}s`;
                    } else {
                        return `${seconds}s`;
                    }
                };
                
                logMessage += `\n处理统计:`;
                logMessage += `\n  - 文档数: ${processed_docs.toLocaleString()}`;
                logMessage += `\n  - 数据量: ${formatSize(processed_size)}`;
                logMessage += `\n  - 耗时: ${formatDuration(duration)}`;
                logMessage += `\n  - 速度: ${docs_per_second.toLocaleString()} docs/s`;
            }
            
            div.innerHTML = logMessage.replace(/\n/g, '<br>');
            logContent.appendChild(div);
            logContent.scrollTop = logContent.scrollHeight;
        });

        socket.on('task_update', function(data) {
            updateTaskInList(data);
        });

        // 添加解析ES完整地址的函数
        function parseEsUrl(url) {
            try {
                const parsedUrl = new URL(url);
                const auth = parsedUrl.username ? {
                    username: decodeURIComponent(parsedUrl.username),
                    password: decodeURIComponent(parsedUrl.password || '')
                } : null;
                
                // 保持路径的原始格式
                let path = parsedUrl.pathname;
                if (path === '/') {
                    path = '';
                }
                
                // 构建配置对象
                const config = {
                    scheme: parsedUrl.protocol.replace(':', ''),
                    host: parsedUrl.hostname,
                    port: parsedUrl.port ? parseInt(parsedUrl.port) : (parsedUrl.protocol === 'https:' ? 443 : 9200),
                    path: path || null,
                    username: auth?.username || null,
                    password: auth?.password || null,
                    verify_certs: false,
                    timeout: 60,
                    max_retries: 3,
                    retry_on_timeout: true,
                    ssl_show_warn: false,
                    headers: {
                        'User-Agent': 'ES-Migration-Tool/1.0'
                    }
                };

                // 如果是HTTPS，添加额外的SSL配置
                if (config.scheme === 'https') {
                    config.ssl_assert_hostname = false;
                    config.ssl_assert_fingerprint = null;
                    config.ssl_version = null;
                    config.ssl_context = null;
                }

                return config;
            } catch (e) {
                console.error('解析ES地址失败:', e);
                return null;
            }
        }

        // 修改获取源配置的函数
        function getSourceConfig() {
            const fullUrl = document.getElementById('sourceFullUrl').value.trim();
            if (fullUrl) {
                const config = parseEsUrl(fullUrl);
                if (config) return config;
            }
            
            return {
                host: document.getElementById('sourceHost').value,
                port: parseInt(document.getElementById('sourcePort').value),
                scheme: 'http',
                username: document.getElementById('sourceUsername').value || null,
                password: document.getElementById('sourcePassword').value || null,
                verify_certs: false,
                timeout: 30,
                max_retries: 3,
                retry_on_timeout: true,
                ssl_show_warn: false
            };
        }

        // 修改获取目标配置的函数
        function getTargetConfig() {
            const fullUrl = document.getElementById('targetFullUrl').value.trim();
            if (fullUrl) {
                const config = parseEsUrl(fullUrl);
                if (config) return config;
            }
            
            return {
                host: document.getElementById('targetHost').value,
                port: parseInt(document.getElementById('targetPort').value),
                scheme: 'http',
                username: document.getElementById('targetUsername').value || null,
                password: document.getElementById('targetPassword').value || null,
                verify_certs: false,
                timeout: 60,
                max_retries: 3,
                retry_on_timeout: true,
                ssl_show_warn: false
            };
        }

        // 添加自动填充分离配置的函数
        function fillSeparateConfig(type) {
            const fullUrlInput = document.getElementById(type + 'FullUrl');
            const config = parseEsUrl(fullUrlInput.value.trim());
            
            if (config) {
                document.getElementById(type + 'Host').value = config.host || '';
                document.getElementById(type + 'Port').value = config.port || 9200;
                document.getElementById(type + 'Username').value = config.username || '';
                document.getElementById(type + 'Password').value = config.password || '';
            }
        }

        async function testConnection() {
            const response = await fetch('/api/test_connection', {
                method: 'POST',
                headers: {'Content-Type': 'application/json'},
                body: JSON.stringify({
                    source_config: getSourceConfig(),
                    target_config: getTargetConfig()
                })
            });
            const result = await response.json();
            alert(result.message);
        }

        async function previewMigration() {
            // 切换到实时日志标签页
            switchTab('logs');
            
            // 清空日志
            const logContainer = document.getElementById('logContent');
            logContainer.innerHTML = '<div>🔍 开始预览迁移...</div>';
            
            const response = await fetch('/api/preview_migration', {
                method: 'POST',
                headers: {'Content-Type': 'application/json'},
                body: JSON.stringify({
                    source_config: getSourceConfig(),
                    target_config: getTargetConfig()
                })
            });
            const result = await response.json();
            
            if (result.success) {
                // 构建详细的预览信息
                let summaryMessage = `\n📊 预览结果总览:\n`;
                summaryMessage += `═══════════════════════════\n`;
                summaryMessage += `📋 总索引数: ${result.total_count}\n`;
                summaryMessage += `✅ 新建索引: ${result.new_indices_count}\n`;
                summaryMessage += `⚠️  重复索引: ${result.existing_indices_count}\n`;
                summaryMessage += `🔄 迁移策略: ${result.migration_strategy}\n`;
                summaryMessage += `📊 源ES版本: ${result.source_version}\n`;
                summaryMessage += `📊 目标ES版本: ${result.target_version}\n`;
                summaryMessage += `🔧 需要转换: ${result.needs_conversion ? '是' : '否'}\n`;
                summaryMessage += `═══════════════════════════`;
                
                // 添加到日志
                const div = document.createElement('div');
                div.style.whiteSpace = 'pre-line';
                div.style.marginTop = '10px';
                div.style.padding = '10px';
                div.style.backgroundColor = '#2d3748';
                div.style.borderRadius = '5px';
                div.textContent = summaryMessage;
                logContainer.appendChild(div);
                
                // 预览完成后延迟1秒切换到迁移操作标签页
                setTimeout(() => {
                    switchTab('migration');
                    // 显示索引选择区域
                    document.getElementById('indexSelection').style.display = 'block';
                    // 更新索引列表
                    displayIndices(result.indices);
                }, 1000);
            } else {
                alert(`❌ 预览失败: ${result.message}`);
            }
        }

        // 添加自动刷新任务列表的功能
        let taskRefreshInterval = null;

        function startTaskRefresh() {
            // 清除可能存在的旧定时器
            if (taskRefreshInterval) {
                clearInterval(taskRefreshInterval);
            }
            
            // 设置新的定时器，每5秒刷新一次任务列表
            taskRefreshInterval = setInterval(refreshTasks, 5000);
        }

        function stopTaskRefresh() {
            if (taskRefreshInterval) {
                clearInterval(taskRefreshInterval);
                taskRefreshInterval = null;
            }
        }

        // 修改switchTab函数，添加任务自动刷新
        function switchTab(tabName) {
            // 停止当前的自动刷新
            stopTaskRefresh();
            
            // 隐藏所有标签页内容
            const tabs = document.querySelectorAll('.tab');
            const contents = document.querySelectorAll('.tab-content');
            
            tabs.forEach(tab => tab.classList.remove('active'));
            contents.forEach(content => content.classList.remove('active'));
            
            // 显示选中的标签页
            document.querySelector(`.tab[onclick="switchTab('${tabName}')"]`).classList.add('active');
            document.getElementById(tabName + '-tab').classList.add('active');
            
            // 如果切换到任务管理标签页，启动自动刷新
            if (tabName === 'tasks') {
                refreshTasks();
                startTaskRefresh();
            }
        }

        // 显示索引列表
        function displayIndices(indices) {
            const indicesList = document.getElementById('indicesList');
            indicesList.innerHTML = '';
            
            indices.forEach(index => {
                const div = document.createElement('div');
                div.className = 'index-item';
                div.innerHTML = `
                    <input type="checkbox" id="index_${index.name}" onchange="updateSelectedCount()">
                    <label for="index_${index.name}" style="flex: 1; cursor: pointer;">
                        <strong>${index.name}</strong>
                        <small style="color: #666; margin-left: 10px;">
                            字段数: ${index.properties_count} | 
                            设置数: ${index.settings_count} |
                            ${index.exists_in_target ? '⚠️ 已存在' : '✅ 新建'}
                        </small>
                    </label>
                    <div style="display: flex; gap: 8px;">
                        <button onclick="viewIndexStructure('${index.name}')" 
                                style="background: #4a90e2; color: white; border: none; border-radius: 4px; padding: 4px 8px; font-size: 12px; cursor: pointer;">
                            查看结构
                        </button>
                    </div>
                `;
                indicesList.appendChild(div);
            });
            
            updateSelectedCount();
        }

        // 更新选中数量
        function updateSelectedCount() {
            const checkboxes = document.querySelectorAll('#indicesList input[type="checkbox"]');
            const checkedCount = Array.from(checkboxes).filter(cb => cb.checked).length;
            document.getElementById('selectedCount').textContent = `已选择: ${checkedCount}`;
            
            // 更新selectedIndices数组
            selectedIndices = Array.from(checkboxes)
                .filter(cb => cb.checked)
                .map(cb => cb.id.replace('index_', ''));
        }

        // 全选索引
        function selectAllIndices() {
            const checkboxes = document.querySelectorAll('#indicesList input[type="checkbox"]');
            checkboxes.forEach(cb => cb.checked = true);
            updateSelectedCount();
        }

        // 取消全选
        function unselectAllIndices() {
            const checkboxes = document.querySelectorAll('#indicesList input[type="checkbox"]');
            checkboxes.forEach(cb => cb.checked = false);
            updateSelectedCount();
        }

        // 开始选择性迁移
        async function startSelectiveMigration() {
            if (selectedIndices.length === 0) {
                alert('请先选择要迁移的索引！');
                return;
            }
            
            if (!confirm(`确认迁移选中的 ${selectedIndices.length} 个索引？`)) return;
            
            const response = await fetch('/api/start_selective_migration', {
                method: 'POST',
                headers: {'Content-Type': 'application/json'},
                body: JSON.stringify({
                    source_config: getSourceConfig(),
                    target_config: getTargetConfig(),
                    selected_indices: selectedIndices,
                    migration_type: 'selected'
                })
            });
            const result = await response.json();
            
            if (result.success) {
                // 切换到任务管理标签页并刷新任务列表
                switchTab('tasks');
            } else {
                alert(`❌ 创建迁移任务失败: ${result.message}`);
            }
        }

        // 修改开始Reindex按钮的点击事件处理函数
        async function startReindex() {
            if (selectedIndices.length === 0) {
                alert('请先选择要reindex的索引！');
                return;
            }
            
            // 显示查询条件设置模态框
            const modal = document.getElementById('queryModal');
            modal.style.display = 'block';
            
            // 聚焦到查询输入框
            document.getElementById('queryDsl').focus();
        }

        // 关闭模态框
        function closeQueryModal() {
            document.getElementById('queryModal').style.display = 'none';
        }

        // 清除查询条件
        function clearQuery() {
            document.getElementById('queryDsl').value = '';
            document.getElementById('queryDsl').focus();
        }

        // 验证JSON并开始Reindex
        async function validateAndStartReindex() {
            const queryDsl = document.getElementById('queryDsl').value.trim();
            let queryConfig = null;
            
            if (queryDsl) {
                try {
                    queryConfig = JSON.parse(queryDsl);
                } catch (e) {
                    alert('查询DSL格式无效，请检查JSON格式是否正确！');
                    return;
                }
            }
            
            if (!confirm(`确认Reindex选中的 ${selectedIndices.length} 个索引？${queryConfig ? '\n注意：将使用设置的查询条件进行过滤。' : ''}`)) return;
            
            // 关闭模态框
            closeQueryModal();
            
            const response = await fetch('/api/start_reindex', {
                method: 'POST',
                headers: {'Content-Type': 'application/json'},
                body: JSON.stringify({
                    source_config: getSourceConfig(),
                    target_config: getTargetConfig(),
                    selected_indices: selectedIndices,
                    query_config: queryConfig
                })
            });
            const result = await response.json();
            
            if (result.success) {
                // 切换到任务管理标签页并刷新任务列表
                switchTab('tasks');
                refreshTasks();
            } else {
                alert(`❌ 创建Reindex任务失败: ${result.message}`);
            }
        }

        // 刷新任务列表
        async function refreshTasks() {
            try {
                const response = await fetch('/api/tasks');
                const result = await response.json();
                
                if (result.success) {
                    displayTasks(result.tasks);
                } else {
                    console.error('获取任务列表失败:', result.message);
                }
            } catch (error) {
                console.error('刷新任务列表失败:', error);
            }
        }

        // 显示任务列表
        function displayTasks(tasks) {
            const tasksList = document.getElementById('tasksList');
            tasksList.innerHTML = '';
            
            if (tasks.length === 0) {
                tasksList.innerHTML = '<div style="text-align: center; color: #666; padding: 40px;">暂无任务</div>';
                return;
            }
            
            tasks.forEach(task => {
                const taskItem = createTaskElement(task);
                tasksList.appendChild(taskItem);
            });
        }

        // 创建任务元素
        function createTaskElement(task) {
            const div = document.createElement('div');
            div.className = `task-item ${task.status}`;
            div.id = `task_${task.id}`;
            
            const statusClass = `status-${task.status}`;
            const statusText = {
                'pending': '等待中',
                'running': '运行中',
                'completed': '已完成',
                'failed': '失败'
            }[task.status] || task.status;
            
            const createdTime = new Date(task.created_at).toLocaleString();
            const duration = task.completed_at ? 
                calculateDuration(task.started_at, task.completed_at) : 
                (task.started_at ? calculateDuration(task.started_at, new Date().toISOString()) : '-');
            
            // 构建详细进度信息
            let detailedProgressHtml = '';
            if (task.type === 'reindex' && task.status === 'running' && task.detailed_progress) {
                const progressEntries = Object.entries(task.detailed_progress);
                if (progressEntries.length > 0) {
                    detailedProgressHtml = `
                        <div style="margin-top: 10px; padding: 10px; background: #f8f9fa; border-radius: 4px;">
                            <strong>📊 详细进度:</strong>
                            ${progressEntries.map(([indexName, progress]) => `
                                <div style="margin: 5px 0; padding: 5px; background: white; border-radius: 3px; font-size: 11px;">
                                    <div style="display: flex; justify-content: space-between; align-items: center;">
                                        <span style="font-weight: bold;">${indexName}</span>
                                        <span style="color: #28a745;">${progress.docs_processed}/${progress.total_docs}</span>
                                    </div>
                                    ${progress.total_docs > 0 ? `
                                        <div style="width: 100%; height: 4px; background: #e9ecef; border-radius: 2px; margin: 3px 0;">
                                            <div style="width: ${(progress.docs_processed / progress.total_docs * 100)}%; height: 100%; background: #28a745; border-radius: 2px;"></div>
                                        </div>
                                        <div style="display: flex; justify-content: space-between; color: #666; font-size: 10px;">
                                            <span>📈 ${progress.rate} docs/秒</span>
                                            <span>⏱️ 剩余: ${progress.eta_formatted}</span>
                                        </div>
                                        ${progress.version_conflicts > 0 ? `
                                            <div style="color: #ffc107; font-size: 10px; margin-top: 2px;">
                                                ⚠️ 版本冲突: ${progress.version_conflicts} (已跳过)
                                            </div>
                                        ` : ''}
                                    ` : '<div style="color: #666; font-size: 10px;">等待开始...</div>'}
                                </div>
                            `).join('')}
                        </div>
                    `;
                }
            }
            
            // 预计完成时间
            let etaHtml = '';
            if (task.estimated_completion && task.status === 'running') {
                const etaTime = new Date(task.estimated_completion).toLocaleString();
                etaHtml = `<div>🕐 预计完成: ${etaTime}</div>`;
            }
            
            // 文档统计
            let docsStatsHtml = '';
            if (task.total_docs > 0) {
                docsStatsHtml = `<div>📄 文档: ${task.processed_docs}/${task.total_docs}</div>`;
            }
            
            div.innerHTML = `
                <div class="task-header">
                    <div>
                        <strong>${task.description}</strong>
                        <small style="color: #666; margin-left: 10px;">${task.type.toUpperCase()}</small>
                        ${task.status === 'running' ? `
                            <button onclick="cancelTask('${task.id}')" 
                                    style="margin-left: 10px; padding: 2px 6px; font-size: 10px; background: #dc3545; color: white; border: none; border-radius: 3px; cursor: pointer;">
                                取消
                            </button>
                        ` : ''}
                        ${task.status === 'completed' || task.status === 'failed' ? `
                            <button onclick="showTaskDetails('${task.id}')" 
                                    style="margin-left: 10px; padding: 2px 6px; font-size: 10px; background: #007bff; color: white; border: none; border-radius: 3px; cursor: pointer;">
                                查看详情
                            </button>
                        ` : ''}
                    </div>
                    <span class="task-status ${statusClass}">${statusText}</span>
                </div>
                
                ${task.status === 'running' ? `
                    <div class="progress-bar">
                        <div class="progress-fill" style="width: ${task.progress}%"></div>
                    </div>
                    <div style="font-size: 12px; color: #666;">
                        进度: ${task.progress}% | ${task.current_step}
                    </div>
                ` : ''}
                
                <div style="font-size: 12px; color: #666; margin-top: 10px;">
                    <div>创建时间: ${createdTime}</div>
                    <div>持续时间: ${duration}</div>
                    ${etaHtml}
                    ${task.total_indices ? `<div>索引数量: ${task.processed_indices || 0}/${task.total_indices}</div>` : ''}
                    ${docsStatsHtml}
                    ${task.results ? `
                        <div style="margin-top: 5px;">
                            结果: 成功 ${task.results.success_count || 0} 个 | 失败 ${task.results.failed_count || 0} 个
                        </div>
                    ` : ''}
                    ${task.error ? `<div style="color: #dc3545; margin-top: 5px;">错误: ${task.error}</div>` : ''}
                </div>
                
                ${detailedProgressHtml}
            `;
            
            return div;
        }

        // 更新任务列表中的任务
        function updateTaskInList(task) {
            const existingTask = document.getElementById(`task_${task.id}`);
            if (existingTask) {
                const newTask = createTaskElement(task);
                existingTask.parentNode.replaceChild(newTask, existingTask);
            } else {
                // 如果任务不存在，且当前在任务标签页，则刷新任务列表
                if (document.getElementById('tasks-tab').classList.contains('active')) {
                    refreshTasks();
                }
            }
        }

        // 计算持续时间
        function calculateDuration(startTime, endTime) {
            if (!startTime) return '-';
            
            const start = new Date(startTime);
            const end = new Date(endTime);
            const diff = Math.floor((end - start) / 1000); // 秒
            
            if (diff < 60) return `${diff}秒`;
            if (diff < 3600) return `${Math.floor(diff / 60)}分${diff % 60}秒`;
            return `${Math.floor(diff / 3600)}时${Math.floor((diff % 3600) / 60)}分`;
        }

        // 取消任务
        async function cancelTask(taskId) {
            if (!confirm('确认取消此任务？正在进行的操作将被中止。')) {
                return;
            }
            
            try {
                const response = await fetch(`/api/task/${taskId}/cancel`, {
                    method: 'POST',
                    headers: {'Content-Type': 'application/json'}
                });
                const result = await response.json();
                
                if (result.success) {
                    alert('✅ 任务已取消');
                    refreshTasks(); // 刷新任务列表
                } else {
                    alert(`❌ 取消失败: ${result.message}`);
                }
            } catch (error) {
                alert(`❌ 取消任务时发生错误: ${error.message}`);
            }
        }

        // 显示任务详情
        async function showTaskDetails(taskId) {
            try {
                const response = await fetch(`/api/task/${taskId}`);
                const result = await response.json();
                
                if (result.success) {
                    const task = result.task;
                    const modal = document.getElementById('taskDetailsModal');
                    const content = document.getElementById('taskDetailsContent');
                    
                    // 构建详情内容
                    let detailsHtml = buildTaskDetailsHtml(task);
                    content.innerHTML = detailsHtml;
                    
                    modal.style.display = 'block';
                } else {
                    alert(`❌ 获取任务详情失败: ${result.message}`);
                }
            } catch (error) {
                alert(`❌ 获取任务详情时发生错误: ${error.message}`);
            }
        }

        // 关闭任务详情弹窗
        function closeTaskDetails() {
            document.getElementById('taskDetailsModal').style.display = 'none';
        }

        // 构建任务详情HTML
        function buildTaskDetailsHtml(task) {
            const createdTime = new Date(task.created_at).toLocaleString();
            const startedTime = task.started_at ? new Date(task.started_at).toLocaleString() : '-';
            const completedTime = task.completed_at ? new Date(task.completed_at).toLocaleString() : '-';
            const duration = task.completed_at ? 
                calculateDuration(task.started_at, task.completed_at) : 
                (task.started_at ? calculateDuration(task.started_at, new Date().toISOString()) : '-');

            let html = `
                <div style="border: 1px solid #ddd; border-radius: 8px; padding: 15px; margin-bottom: 20px; background: #f8f9fa;">
                    <h4 style="margin: 0 0 15px 0; color: #495057;">📊 基本信息</h4>
                    <div style="display: grid; grid-template-columns: 1fr 1fr; gap: 10px;">
                        <div><strong>任务ID:</strong> ${task.id}</div>
                        <div><strong>任务类型:</strong> ${task.type.toUpperCase()}</div>
                        <div><strong>描述:</strong> ${task.description}</div>
                        <div><strong>状态:</strong> 
                            <span style="padding: 2px 8px; border-radius: 4px; background: ${getStatusColor(task.status)}; color: white; font-size: 12px;">
                                ${getStatusText(task.status)}
                            </span>
                        </div>
                        <div><strong>创建时间:</strong> ${createdTime}</div>
                        <div><strong>开始时间:</strong> ${startedTime}</div>
                        <div><strong>完成时间:</strong> ${completedTime}</div>
                        <div><strong>持续时间:</strong> ${duration}</div>
                    </div>
                </div>
            `;

            // 进度信息
            if (task.status === 'running' || task.status === 'completed') {
                html += `
                    <div style="border: 1px solid #ddd; border-radius: 8px; padding: 15px; margin-bottom: 20px; background: #f8f9fa;">
                        <h4 style="margin: 0 0 15px 0; color: #495057;">📈 进度信息</h4>
                        <div style="margin-bottom: 10px;">
                            <strong>总体进度:</strong> ${task.progress}%
                            <div style="width: 100%; height: 8px; background: #e9ecef; border-radius: 4px; margin: 5px 0;">
                                <div style="width: ${task.progress}%; height: 100%; background: #28a745; border-radius: 4px;"></div>
                            </div>
                        </div>
                        <div><strong>当前步骤:</strong> ${task.current_step || '-'}</div>
                        ${task.total_indices ? `<div><strong>索引进度:</strong> ${task.processed_indices || 0}/${task.total_indices}</div>` : ''}
                        ${task.total_docs > 0 ? `<div><strong>文档进度:</strong> ${task.processed_docs}/${task.total_docs}</div>` : ''}
                        ${task.estimated_completion ? `<div><strong>预计完成:</strong> ${new Date(task.estimated_completion).toLocaleString()}</div>` : ''}
                    </div>
                `;
            }

            // 处理结果
            if (task.results) {
                html += `
                    <div style="border: 1px solid #ddd; border-radius: 8px; padding: 15px; margin-bottom: 20px; background: #f8f9fa;">
                        <h4 style="margin: 0 0 15px 0; color: #495057;">📋 处理结果</h4>
                        <div style="display: grid; grid-template-columns: 1fr 1fr 1fr; gap: 15px; text-align: center;">
                            <div style="padding: 10px; background: #d4edda; border-radius: 4px;">
                                <div style="font-size: 24px; font-weight: bold; color: #155724;">${task.results.success_count || 0}</div>
                                <div style="color: #155724;">成功</div>
                            </div>
                            <div style="padding: 10px; background: #f8d7da; border-radius: 4px;">
                                <div style="font-size: 24px; font-weight: bold; color: #721c24;">${task.results.failed_count || 0}</div>
                                <div style="color: #721c24;">失败</div>
                            </div>
                            <div style="padding: 10px; background: #e2e3e5; border-radius: 4px;">
                                <div style="font-size: 24px; font-weight: bold; color: #383d41;">${task.results.total_indices || task.total_indices || 0}</div>
                                <div style="color: #383d41;">总计</div>
                            </div>
                        </div>
                    </div>
                `;
            }

            // 详细进度信息（对于reindex任务）
            if (task.detailed_progress && Object.keys(task.detailed_progress).length > 0) {
                html += `
                    <div style="border: 1px solid #ddd; border-radius: 8px; padding: 15px; margin-bottom: 20px; background: #f8f9fa;">
                        <h4 style="margin: 0 0 15px 0; color: #495057;">📊 详细进度</h4>
                `;
                
                Object.entries(task.detailed_progress).forEach(([indexName, progress]) => {
                    const progressPercent = progress.total_docs > 0 ? (progress.docs_processed / progress.total_docs * 100) : 0;
                    html += `
                        <div style="margin-bottom: 15px; padding: 10px; background: white; border-radius: 4px; border: 1px solid #e9ecef;">
                            <div style="display: flex; justify-content: space-between; align-items: center; margin-bottom: 5px;">
                                <strong>${indexName}</strong>
                                <span style="color: #28a745;">${progress.docs_processed}/${progress.total_docs}</span>
                            </div>
                            ${progress.total_docs > 0 ? `
                                <div style="width: 100%; height: 6px; background: #e9ecef; border-radius: 3px; margin: 5px 0;">
                                    <div style="width: ${progressPercent}%; height: 100%; background: #28a745; border-radius: 3px;"></div>
                                </div>
                                <div style="display: grid; grid-template-columns: 1fr 1fr 1fr 1fr; gap: 10px; font-size: 12px; color: #666;">
                                    <div>📈 速度: ${progress.rate} docs/秒</div>
                                    <div>⏱️ 剩余: ${progress.eta_formatted}</div>
                                    <div>✅ 创建: ${progress.created || 0}</div>
                                    <div>🔄 更新: ${progress.updated || 0}</div>
                                </div>
                                ${progress.version_conflicts > 0 ? `
                                    <div style="margin-top: 5px; padding: 5px; background: #fff3cd; border-radius: 3px; color: #856404; font-size: 12px;">
                                        ⚠️ 版本冲突: ${progress.version_conflicts} (已跳过)
                                    </div>
                                ` : ''}
                            ` : '<div style="color: #666; font-size: 12px;">等待开始...</div>'}
                        </div>
                    `;
                });
                
                html += `</div>`;
            }

            // 索引列表
            if (task.indices && task.indices.length > 0) {
                html += `
                    <div style="border: 1px solid #ddd; border-radius: 8px; padding: 15px; margin-bottom: 20px; background: #f8f9fa;">
                        <h4 style="margin: 0 0 15px 0; color: #495057;">📚 处理索引列表</h4>
                        <div style="max-height: 200px; overflow-y: auto;">
                `;
                
                task.indices.forEach(indexName => {
                    html += `
                        <div style="padding: 5px 10px; margin: 2px 0; background: white; border-radius: 3px; border-left: 3px solid #007bff;">
                            ${indexName}
                        </div>
                    `;
                });
                
                html += `</div></div>`;
            }

            // 索引处理结果
            if (task.index_results && Object.keys(task.index_results).length > 0) {
                html += `
                    <div style="border: 1px solid #ddd; border-radius: 8px; padding: 15px; margin-bottom: 20px; background: #f8f9fa;">
                        <h4 style="margin: 0 0 15px 0; color: #495057;">📊 索引处理结果</h4>
                        <div style="max-height: 300px; overflow-y: auto;">
                `;
                
                Object.entries(task.index_results).forEach(([indexName, result]) => {
                    const statusColor = result.status === 'success' ? '#28a745' : 
                                      result.status === 'failed' ? '#dc3545' : '#007bff';
                    const statusIcon = result.status === 'success' ? '✅' : 
                                     result.status === 'failed' ? '❌' : '🔄';
                    
                    html += `
                        <div style="margin-bottom: 10px; padding: 10px; background: white; border-radius: 4px; border-left: 3px solid ${statusColor};">
                            <div style="display: flex; justify-content: space-between; align-items: center; margin-bottom: 5px;">
                                <span style="font-weight: bold;">${statusIcon} ${indexName}</span>
                                <span style="color: ${statusColor}; font-size: 12px;">${result.status.toUpperCase()}</span>
                            </div>
                            <div style="font-size: 12px; color: #666;">
                                消息: ${result.message}
                                ${result.docs_count > 0 ? `<br>文档数: ${result.docs_count}` : ''}
                                ${result.conflicts > 0 ? `<br>冲突数: ${result.conflicts}` : ''}
                                <br>时间: ${new Date(result.timestamp).toLocaleString()}
                            </div>
                        </div>
                    `;
                });
                
                html += `</div></div>`;
            }

            // 处理日志
            if (task.process_logs && task.process_logs.length > 0) {
                html += `
                    <div style="border: 1px solid #ddd; border-radius: 8px; padding: 15px; margin-bottom: 20px; background: #f8f9fa;">
                        <h4 style="margin: 0 0 15px 0; color: #495057;">📄 处理日志</h4>
                        <div style="max-height: 300px; overflow-y: auto; background: #2d3748; padding: 10px; border-radius: 4px; font-family: monospace; font-size: 12px;">
                `;
                
                task.process_logs.forEach(log => {
                    const levelColor = log.level === 'INFO' ? '#90CDF4' : 
                                     log.level === 'WARNING' ? '#F6E05E' : 
                                     log.level === 'ERROR' ? '#FC8181' : '#f8f9fa';
                    
                    html += `
                        <div style="margin-bottom: 3px; color: ${levelColor};">
                            [${new Date(log.timestamp).toLocaleString()}] [${log.level}] ${log.message}
                        </div>
                    `;
                });
                
                html += `</div></div>`;
            }

            // 错误信息
            if (task.error) {
                html += `
                    <div style="border: 1px solid #dc3545; border-radius: 8px; padding: 15px; margin-bottom: 20px; background: #f8d7da;">
                        <h4 style="margin: 0 0 15px 0; color: #721c24;">❌ 错误信息</h4>
                        <div style="background: white; padding: 10px; border-radius: 4px; color: #721c24; font-family: monospace;">
                            ${task.error}
                        </div>
                    </div>
                `;
            }

            return html;
        }

        // 获取状态颜色
        function getStatusColor(status) {
            switch(status) {
                case 'pending': return '#6c757d';
                case 'running': return '#007bff';
                case 'completed': return '#28a745';
                case 'failed': return '#dc3545';
                default: return '#6c757d';
            }
        }

        // 获取状态文本
        function getStatusText(status) {
            switch(status) {
                case 'pending': return '等待中';
                case 'running': return '运行中';
                case 'completed': return '已完成';
                case 'failed': return '失败';
                default: return status;
            }
        }

        // 查看索引结构
        async function viewIndexStructure(indexName) {
            try {
                const response = await fetch('/api/get_index_structure', {
                    method: 'POST',
                    headers: {
                        'Content-Type': 'application/json'
                    },
                    body: JSON.stringify({
                        source_config: getSourceConfig(),
                        index_name: indexName
                    })
                });

                const data = await response.json();
                if (data.success) {
                    document.getElementById('structureIndexName').textContent = `索引名称: ${indexName}`;
                    
                    // 显示映射和设置
                    const structure = {
                        mappings: data.structure.mappings,
                        settings: data.structure.settings
                    };
                    document.getElementById('structureContent').textContent = JSON.stringify(structure, null, 2);
                    
                    // 显示最新数据
                    document.getElementById('sampleDataContent').textContent = 
                        JSON.stringify(data.structure.latest_document || {}, null, 2);
                    
                    document.getElementById('indexStructureModal').style.display = 'block';
                    
                    // 默认显示映射标签页
                    switchStructureTab('mapping');
                } else {
                    alert('获取索引结构失败: ' + data.message);
                }
            } catch (error) {
                alert('请求失败，请检查网络连接');
                console.error('Error:', error);
            }
        }

        // 切换结构标签页
        function switchStructureTab(tab) {
            // 更新标签状态
            document.getElementById('mappingTab').classList.remove('active');
            document.getElementById('sampleTab').classList.remove('active');
            document.getElementById(tab + 'Tab').classList.add('active');
            
            // 更新内容显示
            document.getElementById('mappingContent').classList.remove('active');
            document.getElementById('sampleContent').classList.remove('active');
            document.getElementById(tab + 'Content').classList.add('active');
        }

        // 关闭索引结构模态框
        function closeIndexStructure() {
            document.getElementById('indexStructureModal').style.display = 'none';
        }

        // 页面加载时自动刷新任务列表
        document.addEventListener('DOMContentLoaded', function() {
            refreshTasks();
            
            // 每10秒自动刷新任务列表（更频繁的更新以显示实时进度）
            setInterval(refreshTasks, 10000);
            
            // 对于正在运行的任务，每5秒刷新一次
            setInterval(function() {
                const runningTasks = document.querySelectorAll('.task-item.running');
                if (runningTasks.length > 0) {
                    refreshTasks();
                }
            }, 5000);
            
            // 点击弹窗外部关闭弹窗
            document.getElementById('taskDetailsModal').addEventListener('click', function(e) {
                if (e.target === this) {
                    closeTaskDetails();
                }
            });

            // 点击索引结构模态框外部关闭弹窗
            document.getElementById('indexStructureModal').addEventListener('click', function(e) {
                if (e.target === this) {
                    closeIndexStructure();
                }
            });

            // 点击模态框外部时关闭
            window.onclick = function(event) {
                const modal = document.getElementById('queryModal');
                if (event.target === modal) {
                    closeQueryModal();
                }
            }

            // 添加完整地址输入框的事件监听
            document.getElementById('sourceFullUrl').addEventListener('blur', () => fillSeparateConfig('source'));
            document.getElementById('targetFullUrl').addEventListener('blur', () => fillSeparateConfig('target'));

            // 在页面加载完成后请求通知权限
            if ("Notification" in window && Notification.permission === "default") {
                Notification.requestPermission();
            }
        });
    </script>
</body>
</html>
